// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: daniel

#pragma once

#include <cstdint>

#include <boost/beast/core/multi_buffer.hpp>
#include <boost/system/error_code.hpp>

#include "coin2/base/request/http2/error.h"
#include "coin2/base/request/session.h"
#include "nghttp2/includes/nghttp2/asio_http2_client.h"

namespace coin2::base::request::http2 {

/*********************************************************************
 * class AsioConntext                                                *
 *********************************************************************/
struct AsioContext {
  using RequestType = coin2::base::request::Session::RequestType;
  using ResponseType = coin2::base::request::Session::ResponseType;
  using CallbackType = coin2::base::request::Session::CallbackType;
  using SessionResponse = coin2::base::request::SessionResponse;
  using Http2RequestType = nghttp2::asio_http2::client::request;

  const int64_t request_id;           // request id
  const Http2RequestType* outer_req;  // nghttp2 request
  RequestType inner_req;              // beast request
  ResponseType inner_res;             // beast response
  CallbackType callback;              // callback
  SessionResponse response;           // parameter for callback
  boost::beast::multi_buffer buff;    // buffer nghttp2 response data
  int weight;                         // for set nghttp2 request priority
  bool invalidated = false;

  explicit AsioContext(int64_t req_id) : request_id{req_id}, outer_req{nullptr} {}
  ~AsioContext() = default;
  AsioContext(const AsioContext&) = delete;
  AsioContext& operator=(const AsioContext&) = delete;

  void Cancel() {
    invalidated = true;
    if (outer_req) {
      // return after cancel nghttp2 request
      // which will trigger callback function
      outer_req->cancel(Nghttp2ErrorCode::NGHTTP2_CANCEL);
      return;
    }
    if (callback) {
      response.error_code = boost::system::errc::make_error_code(boost::system::errc::timed_out);
      response.raw_response = &inner_res;
      response.response_ts = GetCurrentTimestamp();
      callback(response.error_code, &response);
    }
  }
};

}  // namespace coin2::base::request::http2
